<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
	<link rel="stylesheet" href="/assets/css/atom-one-light.css">
    
        <title>一个JS实现的生命游戏</title>
		<link rel="stylesheet" type="text/css" href="/assets/css/002.css">
    
	<link rel="stylesheet" href="/assets/css/font-awesome.min.css">
	<link rel="shortcut icon" href="/assets/img/favicon.ico" type="image/x-icon">
	<link rel="icon" href="/assets/img/favicon.ico" type="image/x-icon">
	<script src="/assets/js/highlight.pack.js"></script>
	<script>hljs.initHighlightingOnLoad();</script>

	

	<script async src="https://www.googletagmanager.com/gtag/js?id=UA-140127665-1"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());

  gtag('config', 'UA-140127665-1');
</script>


</head>
<body>
	<div class="wrapper">
		<div class="default_title">
			<img src="/assets/img/mycomputer.png" />
			
				<h1>NaNg's blog</h1>
			
		</div>
		<ul class="topbar">
	<a href="/pages/me.html"><li><u>A</u>bout</li></a>
	<a href="/pages/links.html"><li><u>L</u>inks</li></a>
	<a href="https://www.dropbox.com/sh/nhy3m3cvojizkk5/AABH8mt5gh3PiBrtWnCxE87ma?dl=0"><li><u>D</u>ropbox</li></a>
</ul>
		<div class="tag_list">
			<ul id="tag-list">
				<li><a href="/" ><img src="/assets/img/disk.png" />(C:)</a>
			<ul>
				
				
				<li><a href="/tag/3d/" title="3d"><img src="/assets/img/folder.ico" />3d</a></li>
				
				<li><a href="/tag/bioinformatics/" title="bioinformatics"><img src="/assets/img/folder.ico" />bioinformatics</a></li>
				
				<li><a href="/tag/notes/" title="notes"><img src="/assets/img/folder.ico" />notes</a></li>
				
				<li><a href="/tag/others/" title="others"><img src="/assets/img/folder.ico" />others</a></li>
				
				<li><a href="/tag/sci-fiction/" title="sci fiction"><img src="/assets/img/folder.ico" />sci fiction</a></li>
				
			</ul>
				</li>
			</ul>
		</div>
		<div class="post_list">
			
				<ul>
					
					<li>
						<a href="https://ani-net-project.gitee.io/index.html" title="AniNet">
								<img class="small-icon" src="/assets/img/aninet.png" title="AniNet" />AniNet
						</a>
					</li>
					
					<li>
						<a href="/examples/boids/index.html" title="Boids">
								<img class="small-icon" src="/assets/img/bird.png" title="Boids" />Boids
						</a>
					</li>
					
					<li>
						<a href="/examples/EM-alg/1_how_it_works.html" title="EM 算法 1. how it works">
								<img class="small-icon" src="/assets/img/notebook.ico" title="EM 算法 1. how it works" />EM 算法 1. how it works
						</a>
					</li>
					
					<li>
						<a href="/examples/pyvm/pyvm_ch0-3_cn.html" title="翻译：Inside Python Virtual Machine（前三章）">
								<img class="small-icon" src="/assets/img/html_ie.ico" title="翻译：Inside Python Virtual Machine（前三章）" />翻译：Inside Python Virtual Machine（前三章）
						</a>
					</li>
					
					<li>
						<a href="/20190624/cLife.html" title="器官工业幻想">
								<img class="small-icon" src="/assets/img/file.ico" title="器官工业幻想" />器官工业幻想
						</a>
					</li>
					
					<li>
						<a href="/examples/pubnet/Network_statistic.html" title="PubNet network statistic">
								<img class="small-icon" src="/assets/img/notebook.ico" title="PubNet network statistic" />PubNet network statistic
						</a>
					</li>
					
					<li>
						<a href="/examples/pubnet/sample.html" title="PubMed bio-conception network example">
								<img class="small-icon" src="/assets/img/net.png" title="PubMed bio-conception network example" />PubMed bio-conception network example
						</a>
					</li>
					
					<li>
						<a href="/examples/hpo_enrich/example_sagd_00055.html" title="HPO enrichment example">
								<img class="small-icon" src="/assets/img/notebook.ico" title="HPO enrichment example" />HPO enrichment example
						</a>
					</li>
					
					<li>
						<a href="/20190222/KS-Test.html" title="Two sample Kolmogorov-Smirnov test">
								<img class="small-icon" src="/assets/img/file.ico" title="Two sample Kolmogorov-Smirnov test" />Two sample Kolmogorov-Smirnov test
						</a>
					</li>
					
					<li>
						<a href="/20190220/threejs-test-page.html" title="three.js test page">
								<img class="small-icon" src="/assets/img/tree.png" title="three.js test page" />three.js test page
						</a>
					</li>
					
					<li>
						<a href="/20181111/hic_data_format.html" title="Hi-C 数据分析结果应该怎么存？">
								<img class="small-icon" src="/assets/img/file.ico" title="Hi-C 数据分析结果应该怎么存？" />Hi-C 数据分析结果应该怎么存？
						</a>
					</li>
					
					<li>
						<a href="/20181010/d3_bubble_chart.html" title="用 D3.js 画一个 bubble chart">
								<img class="small-icon" src="/assets/img/file.ico" title="用 D3.js 画一个 bubble chart" />用 D3.js 画一个 bubble chart
						</a>
					</li>
					
					<li>
						<a href="/20180724/new_kind_slides.html" title="论制作 Slides 的几种姿势">
								<img class="small-icon" src="/assets/img/file.ico" title="论制作 Slides 的几种姿势" />论制作 Slides 的几种姿势
						</a>
					</li>
					
					<li>
						<a href="/slides/test/slideshow.html" title="Markdown Slides Test">
								<img class="small-icon" src="/assets/img/slides.png" title="Markdown Slides Test" />Markdown Slides Test
						</a>
					</li>
					
					<li>
						<a href="/20180221/bioview.html" title="bioView - 一个生信常用文件格式的可读性增强工具">
								<img class="small-icon" src="/assets/img/file.ico" title="bioView - 一个生信常用文件格式的可读性增强工具" />bioView - 一个生信常用文件格式的可读性增强工具
						</a>
					</li>
					
					<li>
						<a href="/20170916/markdown-test-page.html" title="Markdown Test Page">
								<img class="small-icon" src="/assets/img/file.ico" title="Markdown Test Page" />Markdown Test Page
						</a>
					</li>
					
					<li>
						<a href="/20170831/gol-js.html" title="一个JS实现的生命游戏">
								<img class="small-icon" src="/assets/img/file.ico" title="一个JS实现的生命游戏" />一个JS实现的生命游戏
						</a>
					</li>
					
					<li>
						<a href="/20170831/parallel.html" title="这大概是程序串行改并行最简单粗暴的方法">
								<img class="small-icon" src="/assets/img/file.ico" title="这大概是程序串行改并行最简单粗暴的方法" />这大概是程序串行改并行最简单粗暴的方法
						</a>
					</li>
					
					<li>
						<a href="/20170730/learn-docker.html" title="学习Docker">
								<img class="small-icon" src="/assets/img/file.ico" title="学习Docker" />学习Docker
						</a>
					</li>
					
					<li>
						<a href="/20170530/learn-assemble.html" title="学习汇编语言">
								<img class="small-icon" src="/assets/img/file.ico" title="学习汇编语言" />学习汇编语言
						</a>
					</li>
					
					<li>
						<a href="/20170505/schoolnet.html" title="如何在非校园网环境下使用学校文献数据库">
								<img class="small-icon" src="/assets/img/file.ico" title="如何在非校园网环境下使用学校文献数据库" />如何在非校园网环境下使用学校文献数据库
						</a>
					</li>
					
					<li>
						<a href="/20170308/zerotier.html" title="使用ZeroTier搭建虚拟局域网">
								<img class="small-icon" src="/assets/img/file.ico" title="使用ZeroTier搭建虚拟局域网" />使用ZeroTier搭建虚拟局域网
						</a>
					</li>
					
					<li>
						<a href="/20161224/hy-in-brief.html" title="Python生态下的Lisp方言">
								<img class="small-icon" src="/assets/img/file.ico" title="Python生态下的Lisp方言" />Python生态下的Lisp方言
						</a>
					</li>
					
					<li>
						<a href="/20161210/scrapy_douban.html" title="使用Scrapy爬取豆瓣相册">
								<img class="small-icon" src="/assets/img/file.ico" title="使用Scrapy爬取豆瓣相册" />使用Scrapy爬取豆瓣相册
						</a>
					</li>
					
					<li>
						<a href="/20161115/speed_up_python.html" title="加速Python">
								<img class="small-icon" src="/assets/img/file.ico" title="加速Python" />加速Python
						</a>
					</li>
					
				</ul>
			
		</div>
		<div class="post_total">
			
				<div class="left">25 object(s)</div>
			
			<div class="right">&nbsp;</div>
		</div>
	</div>
	
        <div class="content">
			<div class="post_title">
				<img src="/assets/img/file.png" />
				<h1>一个JS实现的生命游戏</h1>
				<a href="/"><div class="btn"><span class="fa fa-times"></span></div></a>
				<div class="btn btn_max"><span class="fa fa-window-maximize"></span></div>
				<div class="btn"><span class="fa fa-window-minimize"></span></div>
			</div>
			<ul class="topbar">
				<li>August 31, 2017</li>
			</ul>
			<div class="post_content" style="max-height: 600px">
				<div class="post_content_inner">
        		<p>之前放假回家的时候，闲着没事做，就学习了一下JS，
想用它实现点什么来检验一下学习成果，
于是就写了一个JS版的<a href="https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life">Game of Life</a>。
代码放在了这个 <a href="https://github.com/Nanguage/YA-game-of-life">repo</a>。
实现了暂停、运行、单步前进、调整fps等功能，还能直接通过鼠标修改cell的状态，效果如下：</p>

<style>
    h1#title {
        text-align: center;
    }
    canvas#world {
        border: 1px solid black;
        margin: auto;
        padding: 0;
        display: block;
    }
    div.control {
        padding-top: 10px;
        text-align: center;
    }
    div.control .basic {
        padding-bottom: 10px;
    }
</style>

<script>
    log = console.log.bind(console)
</script>

<script>
const genMatrix = (width, height, initFunc=randInit) => {
    /**
     * generate matrix(2D Array)
     * initFunc(i, j) : init function of cell in position (i, j),
     *     default random init.
     */
    let m = new Array()
    for (let i = 0; i < height; i++) {
        m[i] = new Array()
        for (let j = 0; j < width; j++) {
            m[i][j] = initFunc(i, j)
        }
    }
    return m
}
const randInit = (i, j) => {
    /**
     * init cell randomly, half chance alive, half chance dead
     */
    return Math.floor(Math.random() * 2)
}
const around = (matrix, y, x, borderSize=1) => {
    /**
     * get the sub-matrix which is around the cell 'matrix[y][x]'
     * borderSize: how many layers around the center, default 1
     */
    let b = borderSize
    let size = b * 2 + 1
    let center = b + 1
    let len_y = matrix.length
    let len_x = matrix[0].length
    let around = genMatrix(size, size, (i, j)=>{
        let pos_y = (y + (i + 1 - center)) % len_y
        let pos_x = (x + (j + 1 - center)) % len_x
        pos_y = (pos_y < 0) ? len_y + pos_y : pos_y
        pos_x = (pos_x < 0) ? len_x + pos_x : pos_x
        let alive = matrix[pos_y][pos_x]
        return alive
    })
    return around
}
const sum2D = (matrix) => {
    /**
     * get the sum value of 2D matrix.
     */
    let sum = 0
    for (let i = 0; i < matrix.length; i++) {
        for (let j =  0; j < matrix[i].length; j++) {
            sum += matrix[i][j]
        }
    }
    return sum
}
const getCenter2D = (matrix) => {
    /**
     * get the center element of matrix
     */
    h = matrix.length
    w = matrix[0].length
    hc = Math.floor(h/2)
    wc = Math.floor(w/2)
    return matrix[hc][wc]
}
const printMatrix = (matrix) => {
    /**
     * print matrix in a easy to read format,
     * for debug
     */
    let str = ""
    for (let i = 0; i < matrix.length; i++) {
        for (let j =  0; j < matrix[i].length; j++) {
            str += matrix[i][j] + " "
        }
        str += "\n"
    }
    log(str)
}
</script>

<script>
const world = (canvas, width=100, height=100, fps=1) => {
    /**
     *  Conway's game of life
     */
    let context = canvas.getContext('2d')
    let status = genMatrix(width, height)
    let w = {
        canvas: canvas,
        context: context,
        width: width,
        height: height,
        status: status,
        fps: fps,
        stoped: false,
    }
    w.update = function() {
        /**
         * update the status according to the rule.
         */
        let next = genMatrix(this.width, this.height, (i, j) => {return 0})
        for (let i = 0; i < this.height; i++) {
            for (let j = 0; j < this.width; j++) {
                let neighbors = around(this.status, i, j)
                let alive = this.status[i][j]
                let n = sum2D(neighbors) - alive
                let nextAlive
                if (alive) {
                    if (n < 2) {
                        nextAlive = 0
                    } else if (n > 3) {
                        nextAlive = 0
                    } else {
                        nextAlive = 1
                    }
                } else {
                    if (n == 3) {
                        nextAlive = 1
                    } else {
                        nextAlive = 0
                    }
                }
                next[i][j] = nextAlive                
                //printMatrix(this.status)
                //printMatrix(neighbors)
                //log(i, j, nextAlive)
            }
        }
        //printMatrix(this.status)
        //printMatrix(next)
        //log('---------------')
        this.status = next.slice()
    }
    w.display = function(colors={ 0: 'white', 1: 'black', }) {
        /**
         * display the world's status in canvas
         */
        let [w, h] = [this.canvas.width/this.width,
                      this.canvas.height/this.height]
        let ctx = this.context
        ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
        for (let i = 0; i < this.height; i++) {
            for (let j = 0; j < this.width; j++) {
                let alive = this.status[i][j]
                ctx.fillStyle = colors[alive]
                ctx.fillRect(j*w, i*h, w, h)
            }
        }
    }
    w.run = function() {
        /**
         * runloop
         */
        if (!this.stoped) {
            this.update()
            this.display()
            setTimeout(() => {
                w.run()
            }, 1000/this.fps)
        } else {
            return
        }
    }

    w.pause = function() {
        /**
         * pause runloop
         */
        this.stoped = true
    }

    w.continue = function() {
        /**
         * revert to runloop
         */
        this.stoped = false
        w.run()
    }

    w.step = function() {
        /**
         * update just one step
         */
        this.update()
        this.display()
    }

    w.clear = function() {
        /**
         * clear status to an empty matrix
         */
        this.status = genMatrix(this.width, this.height,
             (i, j) => {return 0})
    }

    w.init = function(initFunc=randInit) {
        /**
         * init status use a init function
         */
        this.status = genMatrix(this.width, this.height, randInit)
    }

    return w
}
</script>

<script>
const eventHandler = (world) => {
    eh = {
        w: world,
    }
    document.querySelector("#pause").addEventListener("click", () => {
        eh.w.pause()
    })
    document.querySelector("#continue").addEventListener("click", () => {
        eh.w.continue()
    })
    document.querySelector("#step").addEventListener("click", () => {
        eh.w.step()
    })
    document.querySelector("#clear").addEventListener("click", () => {
        eh.w.clear()
        eh.w.display()
    })
    document.querySelector("#init").addEventListener("click", () => {
        eh.w.init()
        eh.w.display()
    })

    document.querySelector("button.set-fps").addEventListener("click", () => {
        let input = document.querySelector("input.set-fps")
        let fps = Number(input.value)
        eh.w.fps = fps
    })

    eh.w.canvas.addEventListener('mousedown', (event) => {
        // change the cell status which has been clicked
        let size = eh.w.canvas.width/eh.w.width
        let y = event.offsetY
        let x = event.offsetX
        let i = Math.floor(y/size) 
        let j = Math.floor(x/size) 
        alive = eh.w.status[i][j]
        eh.w.status[i][j] = alive ? 0 : 1
        eh.w.display()
    })
    return eh
} 
</script>

<div class="app">
    <h1 id="title">Conway's Game of Life</h1>
    <div id="worldview"><div>
    <div class="control">
        <div class="basic">
            <button id="pause">pause</button>
            <button id="continue">continue</button>
            <button id="step">step</button>
            <button id="clear">clear</button>
            <button id="init">init</button>
        </div>
        <div class="fps">
            fps (default 10):
            <input class="set-fps" type="text" />
            <button class="set-fps">OK</button>
        </div>
    </div>
</div>

<script>
    const assert = (condition, message) => {
        if (!condition) {
            message = message || "Assertion failed";
            if (typeof Error !== "undefined") {
                throw new Error(message);
            }
            throw message; // Fallback
        }
    }
    const __test__ = () => {
        let m = [
            [0, 1, 0, 1],
            [1, 1, 0 ,1],
            [1, 1, 1, 1],
            [0, 0, 0, 0],
        ]
        let m0 = around(m, 0, 1)
        let expect_m0 = [
            [0, 0, 0],
            [0, 1, 0],
            [1, 1, 0],
        ]
        let m1 = around(m, 1, 0)
        let expect_m1 = [
            [1, 0, 1],
            [1, 1, 1],
            [1, 1, 1],
        ]
        assert(JSON.stringify(m0) == JSON.stringify(expect_m0),
               "around function error")
        assert(JSON.stringify(m1) == JSON.stringify(expect_m1),
               "around function error")
        assert(sum2D(m0) == 3, "sum2D function error")
        assert(getCenter2D(m0) == 1, "getCenter2D function error")
        //canvas = document.getElementById('world')
        //let w = world(canvas, 4, 4)
        //w.status = m
        //w.display()
        //w.run()
    }
    const __main__ = () => {
        var canvas = document.createElement('canvas')
        canvas.id = "world"
        canvas.width=600
        canvas.height=600
        div = document.getElementById('worldview')
        div.appendChild(canvas)
        w = world(canvas, 200, 200)
        eh = eventHandler(w)
        w.fps = 10
        w.run()
    }
    //__test__()
    __main__() 
</script>

</div></div>

				
					<br>
<hr>
<br>
<div class="donate">
	<p>Email: nanguage@yahoo.com</p>
</div>
				
				</div>
			</div>
		</div>
    
	<script src="/assets/js/001.js"></script>
	<script src="/assets/js/002.js"></script>
	<div class="footer">
		<p>blog theme: <a href="https://github.com/h01000110/windows-95">win95</a></p>
	</div>
</body>
</html>